/**
 * 
 */
package com.trungsi.profile;

import java.io.Serializable;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

/**
 * @author ductrung.tran
 *
 */
@Aspect
public abstract class ProfilingAspect {

	private static Tracer tracer = //new NotifTracer();
			new SimpleTracer(null);
	
	@Pointcut
	public abstract void pointcutDef();
	
	@Around("pointcutDef()")
	public Object trace(ProceedingJoinPoint joinPoint) throws Throwable {
		tracer.traceBegin(joinPoint);
		try {
			return joinPoint.proceed();
		} finally {
			//tracer.traceEnd(joinPoint);
		}
	}

	private static class Stack {		
		long notifId;
		
		
		private Trace current;
		
		public Stack(long notifId) {
			this.notifId = notifId;
		}

		public void addTrace(String name) {
			Trace trace = new Trace(name, current);
			current = trace;
		}
		
		public void endTrace() {
			current.end();
			Trace parent = current.getParent();
			if (parent != null) {
				if (current.getTime() > 20) {
					parent.addChild(current);
				} else {
					parent.addAggregate(current.getTime());
				}
				current = parent;
			}
		}
		
		public boolean isRoot() {
			return current.getParent() == null;
		}
		
		@Override
		public String toString() {
			StringBuilder builder = new StringBuilder();
			current.toString(builder, 0);
			
			return builder.toString();
		}
	}
	
	public static class AggregateTrace implements Serializable {
		private int count;
		private long time;
		
		public void addAggregate(long time) {
			count++;
			this.time += time;
		}
	}
	
	public static class Trace implements Serializable {

		private String name;
		private long start;
		private Trace parent;
		private long end;
		
		private AggregateTrace aggregateTrace = new AggregateTrace();
		
		private List<Trace> children = new LinkedList<Trace>();
		
		public Trace(String name, Trace parent) {
			this.start = System.currentTimeMillis();
			this.name = name;
			this.parent = parent;
		}

		public void toString(StringBuilder builder, int level) {
			long time = getTime();
	
			printSpaces(builder, level);
			builder.append(name + '=' + time + '\n');
			if (aggregateTrace.time > 0) {
				printSpaces(builder, level+1);
				builder.append("aggregate(")
						.append(aggregateTrace.count)
						.append(")=")
						.append(aggregateTrace.time)
						.append('\n');
			}
			childrenToString(builder, level+1);
		}

		public void addAggregate(long time) {
			aggregateTrace.addAggregate(time);
		}
		
		private void childrenToString(StringBuilder builder, int level) {
			for (Trace child : children) {
				child.toString(builder, level);
			}
		}

		private void printSpaces(StringBuilder builder, int level) {
			for (int i = 1; i <= level; i++) {
				builder.append("  ");
			}
			builder.append(level).append(' ');
		}

		private void addChild(Trace trace) {
			children.add(trace);
		}

		public Trace getParent() {
			return parent;
		}

		public List<Trace> getChildren() {
			return children;
		}
		
		public void end() {
			this.end = System.currentTimeMillis();
		}
		
		public long getStart() {
			return start;
		}
		
		public long getEnd() {
			return end;
		}
		
		public long getTime() {
			return end - start;
		}
		
		@Override
		public String toString() {
			long time = getTime();
			return name + "=" + time + "\n";
		}
	}
}
